/**
 * project7
 *
 * brief description of the program
 *
 * This program is released to public domain according to
 * author's wish, you can view full license at
 * http://tomatoeskit.org/upload/PublicDomainLicense.txt
 *
 * @author Fangning CHENG (Alpha.L Cheng) <chengf@purdue.edu>
 *
 * @lab LM4
 *
 * @date Nov 9, 2013
 *
 */
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;

import edu.purdue.cs.cs180.channel.*;

public class LocBasedServer extends Server {

    Map<String, BldDist[]> distanceTable;   
    
    Map<String, LinkedList<RequesterUnit>> requesters =
            new TreeMap<String, LinkedList<RequesterUnit>>();
    Map<String, LinkedList<Integer>> volunteers = new TreeMap<String, LinkedList<Integer>>();
    
    protected class BldDist {
        public final String buildingName;
        public final int time;
        public BldDist(String buildingName, int time)
        {
            this.buildingName = buildingName;
            this.time = time;
        }       
    }
    
    protected class RequesterUnit {
        public final int requesterID;
        public final String urgency;
        public RequesterUnit(int requesterID, String urgency) {
            this.requesterID = requesterID;
            this.urgency = urgency;
        }
    }
    
    LocBasedServer(Channel cnl) {
        super(cnl);
        System.err.println("\tLocation Based Server is running ...");
    }
    
    @Override
    public void messageReceived(String message, int clientID) {       
        String msg[] = message.split("\\s+");
        int msgLen = msg.length;

        // Invalid message
        if (msgLen < 2)
            return;
        
        System.err.println(String.format("\t\tMessage from %d: %s", clientID, message));

        int requestType = 0;    // 0: null 1: requester 2: volunteer
        String location = null;
        String urgency = null;
        
        // Request message
        if (msg[0].equals("REQUEST")) {
            if (msgLen < 3) { // Broken message
                System.err.println("\t\t\tBroken REQUEST message");
                return;
            } 
            
            if (!requesters.containsKey(msg[1]))
                // What the hell. Prefer C++ [] syntax
                requesters.put(msg[1], new LinkedList<RequesterUnit>());
            requesters.get(msg[1]).offer(new RequesterUnit(clientID, msg[2]));
            System.err.println(String.format("\t\t\tRequester %d added to queue %s", clientID, msg[1]));
            
            requestType = 1;
            location = msg[1];
            urgency = msg[2];
        }

        // Volunteer message
        if (msg[0].equals("VOLUNTEER")) {
            if (!volunteers.containsKey(msg[1]))
                volunteers.put(msg[1], new LinkedList<Integer>());
            volunteers.get(msg[1]).offer(clientID);
            System.err.println(String.format("\t\t\tVolunteer %d added to queue %s", clientID, msg[1]));
            
            requestType = 2;
            location = msg[1];
        }

        if (requestType == 0) return;   // What the hell message is it?
        
        processQueue(requestType, location, urgency);

    }
    
    /**
     * This function must make sure that either Volunteer or Requester queue 
     * has no more than 1 element. 
     * 
     * @param requestType 0 = ?; 1 = Requester; 2 = Volunteer
     * @param location
     * @param urgency
     */
    protected void processQueue(int requestType, String location, String urgency) {
        if (requestType == 1) { // Requester
            for (BldDist buildingDist : distanceTable.get(location)) {
                LinkedList<Integer> volunteerList = volunteers.get(buildingDist.buildingName);

                if (volunteerList == null || volunteerList.isEmpty())    // This building has no available volunteer
                    continue;
                else {  // Pair up
                    int volunteerID = volunteerList.poll();
                    RequesterUnit requester = requesters.get(location).poll();
                    System.err.println(String.format(
                            "\t\tVolunteer %d polled from Queue %s", volunteerID, buildingDist.buildingName));
                    System.err.println(String.format(
                            "\t\tRequester %d polled from Queue %s", requester.requesterID, location));

                    if (!sendMessages(volunteerID, requester.requesterID, buildingDist.time, 
                             location, requester.urgency, buildingDist.buildingName))
                        System.err.println("\t\tWhen processing requester from Requester " + requester.requesterID);
                    break;      // VERY IMPORTANT! DON'T FORGET!
                }
            }
            return;
        }

        if (requestType == 2) { // Volunteer
            for (BldDist buildingDist : distanceTable.get(location)) {
                LinkedList<RequesterUnit> requesterList = requesters.get(buildingDist.buildingName);
                
                if (requesterList == null || requesterList.isEmpty()) // This building does not have a requester
                    continue;
                else {  // Pair up
                    int volunteerID = volunteers.get(location).poll();  // Volunteer just pushed to queue
                    RequesterUnit requester = requesterList.poll();

                    System.err.println(String.format(
                            "\t\tVolunteer %d polled from Queue %s", volunteerID, location));
                    System.err.println(String.format(
                            "\t\tRequester %d polled from Queue %s", requester.requesterID, buildingDist.buildingName));

                    
                    if (!sendMessages(volunteerID, requester.requesterID, buildingDist.time, 
                            buildingDist.buildingName, requester.urgency, location))
                        System.err.println("\t\t\tWhen processing request from Volunteer " + volunteerID);
                    break;      // VERY IMPORTANT! DON'T FORGET IT!
                }
            }
            return;
        }
        
        System.err.println("\tprocessQueue(): Broken requestType");
        return;
    }
    
    @Override
    protected void initialize()
    {
        super.initialize();
        distanceTable = new TreeMap<String, BldDist[]>();
        distanceTable.put("CL50", 
                new BldDist[] {new BldDist("CL50", 0), 
                    new BldDist("PUSH", 4), new BldDist("PMU", 5),
                    new BldDist("LWSN", 6), new BldDist("EE", 8)});
        distanceTable.put("EE",     
                new BldDist[] {new BldDist("EE", 0), 
                    new BldDist("PMU", 2), new BldDist("LWSN", 4), 
                    new BldDist("PUSH", 5), new BldDist("CL50", 8)});
        distanceTable.put("LWSN",
                new BldDist[] {new BldDist("LWSN", 0), 
                    new BldDist("PUSH", 1), new BldDist("PMU", 3), 
                    new BldDist("EE", 4), new BldDist("CL50", 6)});
        distanceTable.put("PMU",    
                new BldDist[] {new BldDist("PMU", 0),
                    new BldDist("EE", 2), new BldDist("LWSN", 3),
                    new BldDist("CL50", 5), new BldDist("PUSH", 7)});
        distanceTable.put("PUSH",
                new BldDist[] {new BldDist("PUSH", 0), 
                    new BldDist("LWSN", 1), new BldDist("CL50", 4), 
                    new BldDist("EE", 5), new BldDist("PMU", 7)});
    }
}


